﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using FasterWPF;
using System.Windows.Data;

namespace FasterWPF
{
    public static class FlatButtonHelpers
    {
        public enum GeometryButtonSize {
            Small = 0,
            Large = 1
        }

        public class FlatGeometryButtonEventArgs : EventArgs
        {
            private bool _isMouseDirectlyOver;

            public FlatGeometryButtonEventArgs(bool isMouseDirectlyOver)
            {
                this._isMouseDirectlyOver = isMouseDirectlyOver;
            }

            public object IsMouseDirectlyOver
            {
                get
                {
                    return this._isMouseDirectlyOver;
                }
            }
        }

        /// <summary>
        /// Create a new Border (FlatGeometryButton) applying the FlatDesignStyle with an Geometry Path (Icon) in the middle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static Grid CreateFlatGeometryButton(string geometryPath, GeometryButtonSize geometryButtonSize, Brush backgroundBrush, Brush pathBrush, Brush borderBrush, Brush onMouseOverBackgroundBrush, Brush onMouseOverPathBrush, Thickness borderThickness, Thickness pathPadding, object toolTipContent, Action<object, FlatGeometryButtonEventArgs> onMouseOverAction, Action<object, MouseButtonEventArgs> actionOnClick)
        {
            Grid arrowImageButton3 = null;
            double width = 26D;
            double height = 26D;
            double gridWidth = 48D;
            double gridHeight = 48D;

            if(((int)geometryButtonSize)==(int)GeometryButtonSize.Small)
            {
                width = 13D;
                height = 13D;
                gridWidth = 24D;
                gridHeight = 24D;
            }

                    //Geometry example sourced from Syncfusion Metro Studio 2 - copyright free icons
                    TransformGroup traGrp3 = new TransformGroup();
                    traGrp3.Children.Add(new RotateTransform() { Angle = 0 });
                    traGrp3.Children.Add(new ScaleTransform() { ScaleX = 1, ScaleY = 1 });

                    var myPath3 = new Path() { Data = Geometry.Parse(geometryPath), Stretch = Stretch.Uniform, Fill = pathBrush, Width = width, Height = height, Margin = new Thickness(0), RenderTransformOrigin = new Point(0.5, 0.5), RenderTransform = traGrp3 };

                    arrowImageButton3 = FlatButtonHelpers.CreateGridContainerForPath(gridWidth, gridHeight, backgroundBrush, borderBrush, borderThickness, myPath3,
                            (sender, e) =>
                            {
                                //Grid source1 = arrowImageButton3;
                                //Path source2 = myPath3;

                                Grid source1 = null;
                                Path source2 = null;
                                if (sender is Grid)
                                {
                                    source1 = sender as Grid;
                                    source2 = source1.Children[0] as Path;
                                }
                                else
                                {
                                    source2 = sender as Path;
                                    source1 = (sender as Path).Parent as Grid;
                                }
                                
                                if (source1.IsMouseDirectlyOver || source2.IsMouseDirectlyOver)
                                // if (((bool)e.NewValue)) - this doesn't work. It fires twice, once with true and once with false on mouseover
                                {
                                    source1.Background = onMouseOverBackgroundBrush; source2.Fill = onMouseOverPathBrush;
                                    onMouseOverAction(sender, new FlatGeometryButtonEventArgs(((bool)e.NewValue)));
                                }
                                else
                                {
                                    source1.Background = backgroundBrush;
                                    source2.Fill = pathBrush;
                                }
                            },
                            (sender, ev) =>
                            {
                                actionOnClick(sender, ev);
                            }
                        , pathPadding, toolTipContent);            

            return arrowImageButton3;
        }

        /// <summary>
        /// Add a new Border (FlatButton) applying the FlatDesignStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static InnerChain<T> AddFlatButton<T>(this InnerChain<T> source, int row, int col, double width, double height, string textCaption, Brush border, Thickness thickness, Brush background, CornerRadius cornerRadius, Thickness padding, Thickness margin, Brush mouseOverColor, Brush foregroundColor, Brush foregroundMouseOverColor, FontSettings regularFontSettings, FontSettings mouseOverFontSettings, Action<object, MouseButtonEventArgs> actionOnClick)
               where T : FrameworkElement
        {
            Border flatButton = new Border();
            flatButton.Initialize(width, height, ContainerType.HorizontalPanel, border, thickness, background, cornerRadius, padding, margin);
         
            flatButton.SubscribeMouseSingleClickOnBorder<Border>((o, ev) =>
            {
                actionOnClick(o, ev);
            });
            flatButton.SetMouseOverColorOnBorder(mouseOverColor);
            flatButton.SetMouseOverForeGroundAndRegularForegroundOnBorderWithFonts(foregroundMouseOverColor, foregroundColor, regularFontSettings, mouseOverFontSettings);
            var buttonBorder = flatButton.BeginComposite().AddText<Border>(0, 0, textCaption).EndComposite<Border, BorderArgs>(null);
            //TextBlock x = (flatButton.GetChildrenFromComposite<Border>(buttonBorder.GetKey())[0] as TextBlock);

            source.Item3.Add(((container) =>
            {
                return container.AddExisting<Border, T>(source.Item2, row, col, flatButton);
            }));
            return source;
        }

        /// <summary>
        /// Add a new Border (FlatButton) applying the FlatDesignStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static InnerChain<T> AddFlatButton<T>(this InnerChain<T> source, int row, int col, double width, double height, string textCaption, Brush border, Thickness thickness, Brush background, CornerRadius cornerRadius, Thickness padding, Thickness margin, Brush mouseOverColor, Brush foregroundColor, Brush foregroundMouseOverColor, string fontFamily, double fontSize, FontWeight fontWeight, FontStyle fontStyle, Action<object, MouseButtonEventArgs> actionOnClick)
               where T : FrameworkElement
        {
            Border flatButton = new Border();
            flatButton.Initialize(width, height, ContainerType.HorizontalPanel, border, thickness, background, cornerRadius, padding, margin);
            flatButton.BeginSettings<Border>().SetFont(0, 0, fontFamily, fontSize, fontWeight, fontStyle).EndSettings();
            flatButton.SubscribeMouseSingleClickOnBorder<Border>((o, ev) =>
            {
                actionOnClick(o, ev);
            });
            flatButton.SetMouseOverColorOnBorder(mouseOverColor);
            flatButton.SetMouseOverForeGroundAndRegularForegroundOnBorder(foregroundMouseOverColor, foregroundColor);
            var buttonBorder = flatButton.BeginComposite().AddText<Border>(0, 0, textCaption).EndComposite<Border, BorderArgs>(null);
            //TextBlock x = (flatButton.GetChildrenFromComposite<Border>(buttonBorder.GetKey())[0] as TextBlock);

            source.Item3.Add(((container) =>
            {
                return container.AddExisting<Border, T>(source.Item2, row, col, flatButton);
            }));
            return source;
        }

        /// <summary>
        /// Add a new Border (FlatButton) applying the FlatDesignStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static InnerChain<T> AddFlatButton<T>(this InnerChain<T> source, int row, int col, double width, double height, string textCaption, Brush border, Thickness thickness, Brush background, CornerRadius cornerRadius, Thickness padding, Thickness margin, Brush mouseOverColor, Brush foregroundColor, Brush foregroundMouseOverColor, Action<object, MouseButtonEventArgs> actionOnClick)
               where T : FrameworkElement
        {
            Border flatButton = new Border();
            flatButton.Initialize(width, height, ContainerType.HorizontalPanel, border, thickness, background, cornerRadius, padding, margin);

            flatButton.SubscribeMouseSingleClickOnBorder<Border>((o, ev) =>
            {
                actionOnClick(o, ev);
            });
            flatButton.SetMouseOverColorOnBorder(mouseOverColor);
            flatButton.SetMouseOverForeGroundAndRegularForegroundOnBorder(foregroundMouseOverColor, foregroundColor);
            var buttonBorder = flatButton.BeginComposite().AddText<Border>(0, 0, textCaption).EndComposite<Border, BorderArgs>(null);
            //TextBlock x = (flatButton.GetChildrenFromComposite<Border>(buttonBorder.GetKey())[0] as TextBlock);
            
            source.Item3.Add(((container) =>
            {
                return container.AddExisting<Border, T>(source.Item2, row, col, flatButton);
            }));
            return source;
        }
         
        /// <summary>
        /// Add a new Border (FlatButton) applying the FlatDesignStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static InnerChain<T> AddFlatButton<T>(this InnerChain<T> source, int row, int col, double width, double height, string textCaption, Brush border, Thickness thickness, Brush background, CornerRadius cornerRadius, Thickness padding, Thickness margin, Brush mouseOverColor, Brush foregroundMouseOverColor, Action<object, MouseButtonEventArgs> actionOnClick)
               where T : FrameworkElement
        {
            Border flatButton = new Border();
            flatButton.Initialize(width, height, ContainerType.HorizontalPanel, border, thickness, background, cornerRadius, padding, margin);

            flatButton.SubscribeMouseSingleClickOnBorder<Border>((o, ev) =>
            {
                actionOnClick(o, ev);
            });
            flatButton.SetMouseOverColorOnBorder(mouseOverColor);
            flatButton.SetMouseOverForeGroundOnBorder(foregroundMouseOverColor);
            flatButton.BeginComposite().AddText<Border>(0, 0, textCaption).EndComposite<Border, BorderArgs>(null);
         
            source.Item3.Add(((container) =>
            {
                return container.AddExisting<Border, T>(source.Item2, row, col, flatButton);
            }));
            return source;
        }


        /// <summary>
        /// Add a new Border (FlatButton) applying the FlatDesignStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static InnerChain<T> AddFlatButton<T>(this InnerChain<T> source, int row, int col, double width, double height, string textCaption, Brush border, Thickness thickness, Brush background, CornerRadius cornerRadius, Thickness padding, Thickness margin, Brush mouseOverColor, Action<object, MouseButtonEventArgs> actionOnClick)
               where T : FrameworkElement
        {
            Border flatButton = new Border();
            flatButton.Initialize(width, height, ContainerType.HorizontalPanel, border, thickness, background, cornerRadius, padding, margin);

            flatButton.SubscribeMouseSingleClickOnBorder<Border>((o, ev) =>
            {
                actionOnClick(o, ev);
            });
            flatButton.SetMouseOverColorOnBorder(mouseOverColor);
            flatButton.BeginComposite().AddText<Border>(0, 0, textCaption).EndComposite<Border, BorderArgs>(null);

            source.Item3.Add(((container) =>
            {
                return container.AddExisting<Border, T>(source.Item2, row, col, flatButton);
            }));
            return source;
        }

        /// <summary>
        /// Create a new Border (FlatButton) applying the FlatDesignStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static Border NewFlatButton(double width, double height, string textCaption, Brush border, Thickness thickness, Brush background, CornerRadius cornerRadius, Thickness padding, Thickness margin, Brush mouseOverColor, Action<object, MouseButtonEventArgs> actionOnClick)
       {
            Border flatButton = new Border();
            flatButton.Initialize(width, height, ContainerType.HorizontalPanel, border, thickness, background, cornerRadius, padding, margin);

            flatButton.SubscribeMouseSingleClickOnBorder<Border>((o, ev) =>
            {
                actionOnClick(o, ev);
            });
            flatButton.SetMouseOverColorOnBorder(mouseOverColor);

            flatButton.BeginComposite().AddText<Border>(0, 0, textCaption).EndComposite<Border, BorderArgs>(null);

            return flatButton;
        }

        /// <summary>
        /// Create a new Border (FlatButton) applying the FlatDesignStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static Border NewFlatButton(string myGuid, double width, double height, string textCaption, Brush border, Thickness thickness, Brush background, CornerRadius cornerRadius, Thickness padding, Thickness margin, Brush mouseOverColor, Action<object, MouseButtonEventArgs> actionOnClick)
        {
            Border flatButton = new Border();
            flatButton.Initialize(width, height, ContainerType.HorizontalPanel, border, thickness, background, cornerRadius, padding, margin);

            flatButton.SubscribeMouseSingleClickOnBorder<Border>((o, ev) =>
            {
                actionOnClick(o, ev);
            });
            flatButton.SetMouseOverColorOnBorder(mouseOverColor);
            flatButton.BeginComposite(myGuid).AddText<Border>(0, 0, textCaption).EndComposite<Border, BorderArgs>(null);

            return flatButton;
        }

        /// <summary>
        /// Create a new Border (FlatButton) applying the FlatDesignStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static Border NewFlatButton(double width, double height, string textCaption, VerticalAlignment vertAlign, HorizontalAlignment horizAlign, Brush border, Thickness thickness, Brush background, CornerRadius cornerRadius, Thickness padding, Thickness margin, Brush mouseOverColor, Action<object, MouseButtonEventArgs> actionOnClick)
       {
            Border flatButton = new Border();
            flatButton.Initialize(width, height, ContainerType.Grid, border, thickness, background, cornerRadius, padding, margin);
            flatButton.SetCompositeGridDimensions<Border>(1, 1, flatButton.Width, GridUnitType.Pixel,flatButton.Height, GridUnitType.Pixel);

            flatButton.SubscribeMouseSingleClickOnBorder<Border>((o, ev) =>
            {
                actionOnClick(o, ev);
            });
            flatButton.SetMouseOverColorOnBorder(mouseOverColor);           
            flatButton.BeginComposite().AddText<Border>(0, 1, textCaption).EndComposite<Border, BorderArgs>(null);
 
            var textB =  flatButton.GetChildLookupDictionary<Border>().Values.First<object>() as TextBlock;
            if (textB != null)
            {
                textB.HorizontalAlignment = horizAlign;
                textB.VerticalAlignment = vertAlign;
            }
            
            return flatButton; 
        }

        /// <summary>
        /// Create a new Border (FlatButton) applying the FlatDesignStyle
        /// </summary>
        /// <param name="txb"></param>
        /// <returns></returns>
        public static Border NewFlatButton(string myGuid, double width, double height, string textCaption,  VerticalAlignment vertAlign, HorizontalAlignment horizAlign, Brush border, Thickness thickness, Brush background, CornerRadius cornerRadius, Thickness padding, Thickness margin, Brush mouseOverColor, Action<object, MouseButtonEventArgs> actionOnClick)
        {
            Border flatButton = new Border();
            flatButton.Initialize(width, height, ContainerType.Grid, border, thickness, background, cornerRadius, padding, margin);
            flatButton.SetCompositeGridDimensions<Border>(1, 1, flatButton.Width, GridUnitType.Pixel, flatButton.Height, GridUnitType.Pixel);

            flatButton.SubscribeMouseSingleClickOnBorder<Border>((o, ev) =>
            {
                actionOnClick(o, ev);
            });
            flatButton.SetMouseOverColorOnBorder(mouseOverColor);
            flatButton.BeginComposite(myGuid).AddText<Border>(0, 0, textCaption).EndComposite<Border, BorderArgs>(null);
            
            var textB = flatButton.GetChildLookupDictionary<Border>().Values.First<object>() as TextBlock;
            if (textB != null)
            {
                textB.HorizontalAlignment = horizAlign;
                textB.VerticalAlignment = vertAlign;
            }

            return flatButton;
        }

        /// <summary>
        /// Used by Flat Geometry Button examples sourced from Syncfusion Metro Studio 2 - copyright free icons
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="brush"></param>
        /// <param name="path"></param>
        /// <param name="onMouseOverAction"></param>
        /// <param name="actionOnClick"></param>
        /// <returns></returns>
        public static Grid CreateGridContainerForPath(double width, double height, Brush brush, Path path, Action<Object, ValueChangedEventArgs> onMouseOverAction, Action<object, MouseButtonEventArgs> actionOnClick)
        {
            return CreateGridContainerForPath(width, height, brush, path, onMouseOverAction, actionOnClick, new Thickness(0), null);
        }

        /// <summary>
        /// Used by Flat Geometry Button examples sourced from Syncfusion Metro Studio 2 - copyright free icons
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="brush"></param>
        /// <param name="path"></param>
        /// <param name="onMouseOverAction"></param>
        /// <param name="actionOnClick"></param>
        /// <param name="toolTipContent"></param>
        /// <returns></returns>
        public static Grid CreateGridContainerForPath(double width, double height, Brush brush, Path path, Action<Object, ValueChangedEventArgs> onMouseOverAction, Action<object, MouseButtonEventArgs> actionOnClick, object toolTipContent)
        {
            return CreateGridContainerForPath(width, height, brush, path, onMouseOverAction, actionOnClick, new Thickness(0), toolTipContent);
        }

        /// <summary>
        /// Used by Flat Geometry Button examples sourced from Syncfusion Metro Studio 2 - copyright free icons
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="brush"></param>
        /// <param name="path"></param>
        /// <param name="onMouseOverAction"></param>
        /// <param name="actionOnClick"></param>
        /// <param name="padding"></param>
        /// <returns></returns>
        public static Grid CreateGridContainerForPath(double width, double height, Brush brush, Path path, Action<Object, ValueChangedEventArgs> onMouseOverAction, Action<object, MouseButtonEventArgs> actionOnClick, Thickness padding)
        {
            return CreateGridContainerForPath(width, height, brush, path, onMouseOverAction, actionOnClick, padding, null);
        }

        /// <summary>
        /// Used by Flat Geometry Button examples sourced from Syncfusion Metro Studio 2 - copyright free icons
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="brush"></param>
        /// <param name="path"></param>
        /// <param name="onMouseOverAction"></param>
        /// <param name="actionOnClick"></param>
        /// <param name="padding"></param>
        /// <param name="toolTipContent"></param>
        /// <returns></returns>
        public static Grid CreateGridContainerForPath(double width, double height, Brush brush, Path path, Action<Object, ValueChangedEventArgs> onMouseOverAction, Action<object, MouseButtonEventArgs> actionOnClick, Thickness padding, object toolTipContent)
        {
            if (padding != null)
            {
                path.Margin = padding;
            }

            Grid myGrid = new Grid();
            myGrid.Initialize(width, height, ContainerType.Grid);
            myGrid.SetCompositeGridDimensionsWithRowHt<Grid>(1, 1, height, GridUnitType.Pixel, width, GridUnitType.Pixel);

            myGrid.BeginSettings<Grid>()
                .SetItemBorderSettings(width, height, Brushes.Silver, new Thickness(0))
                .EndSettings();
            
            var myComp = myGrid.BeginComposite()
                .AddExisting<Path, Grid>(0, 0, path)
                .SubscribeDependencyEventOnChild<Path, Grid>(0, 0, "IsMouseDirectlyOverChanged", (o, ev) =>
                {
                    onMouseOverAction(o, ev);
                })
                .SubscribeMouseSingleClickOnContainer<Grid, Grid>((o, ev) =>
                {
                    actionOnClick(o, ev);
                })
                .SubscribeDependencyEventOnContainer<Grid, Grid>("IsMouseDirectlyOverChanged",  (o, ev) =>
                {
                    onMouseOverAction(o, ev);
                })
                .EndComposite<Grid, GridArgs>(new GridArgs(0, 0));

            myComp.GetParentFromComposite<Grid>().GetContainerFromComposite<Grid, Grid>(myComp).Background = brush; //set grid background to brush

            if (toolTipContent != null)
            {
                (myGrid as FrameworkElement).ToolTip = new ToolTip() { Content = toolTipContent };
            }

            return myGrid;
        }

        /// <summary>
        /// Used by Flat Geometry Button examples sourced from Syncfusion Metro Studio 2 - copyright free icons
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="brush"></param>
        /// <param name="borderBrush"></param>
        /// <param name="borderThickness"></param>
        /// <param name="path"></param>
        /// <param name="onMouseOverAction"></param>
        /// <param name="actionOnClick"></param>
        /// <param name="padding"></param>
        /// <param name="toolTipContent"></param>
        /// <returns></returns>
        public static Grid CreateGridContainerForPath(double width, double height, Brush brush, Brush borderBrush, Thickness borderThickness, Path path, Action<Object, ValueChangedEventArgs> onMouseOverAction, Action<object, MouseButtonEventArgs> actionOnClick, Thickness padding, object toolTipContent)
        {
            if (padding != null)
            {
                path.Margin = padding;
            }

            Grid myGrid = new Grid();
            myGrid.Initialize(width, height, ContainerType.Grid);
            myGrid.SetCompositeGridDimensionsWithRowHt<Grid>(1, 1, height, GridUnitType.Pixel, width, GridUnitType.Pixel);

            myGrid.BeginSettings<Grid>()
                .SetItemBorderSettings(width, height, borderBrush, borderThickness)
                .EndSettings();

            var myComp = myGrid.BeginComposite()
                .AddExisting<Path, Grid>(0, 0, path)
                .SubscribeDependencyEventOnChild<Path, Grid>(0, 0, "IsMouseDirectlyOverChanged", (o, ev) =>
                {
                    onMouseOverAction(o, ev);
                })
                .SubscribeMouseSingleClickOnContainer<Grid, Grid>((o, ev) =>
                {
                    actionOnClick(o, ev);
                })
                .SubscribeDependencyEventOnContainer<Grid, Grid>("IsMouseDirectlyOverChanged", (o, ev) =>
                {
                    onMouseOverAction(o, ev);
                })
                .EndComposite<Grid, GridArgs>(new GridArgs(0, 0));

            myComp.GetParentFromComposite<Grid>().GetContainerFromComposite<Grid, Grid>(myComp).Background = brush; //set grid background to brush

            if (toolTipContent != null)
            {
                (myGrid as FrameworkElement).ToolTip = new ToolTip() { Content = toolTipContent };
            }

            return myGrid;
        }
            
    }//end of class
}//end of namespace
